package com.lianqi.emcpframework.ddd.gencode.plugin;

import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.ElementFilter;
import javax.validation.Valid;

import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.constraints.*;

import com.lianqi.emcpframework.ddd.gencode.EnableGenForAggregate;
import com.lianqi.emcpframework.ddd.gencode.EnableGenForEntity;
import com.lianqi.emcpframework.ddd.gencode.creator.GenCreator;
import com.lianqi.emcpframework.ddd.gencode.creator.GenCreatorIgnore;
import com.lianqi.emcpframework.ddd.gencode.creator.GenCreatorParser;
import com.lianqi.emcpframework.ddd.gencode.creator.GenCreatorType;
import com.lianqi.emcpframework.ddd.gencode.creator.support.meta.CreatorMeta;
import com.lianqi.emcpframework.ddd.gencode.creator.support.parser.CreatorMetaParser;
import com.lianqi.emcpframework.ddd.gencode.creator.support.writer.CreatorWriter;
import com.lianqi.emcpframework.ddd.gencode.creator.support.writer.DataOptionalBasedCreatorMethodWriter;
import com.lianqi.emcpframework.ddd.gencode.creator.support.writer.JavaBeanBasedCreatorMethodWriter;
import com.lianqi.emcpframework.ddd.gencode.define.JavaSource;
import com.squareup.javapoet.*;

import lombok.Data;

public class GenCreatorPlugin extends AbstractProcessorPlugin {
    private final CreatorMetaParser metaParser = new CreatorMetaParser();
    // private final DataOptionalBasedCreatorMethodWriter dataOptionalBasedCreatorMethodWriter = new
    // DataOptionalBasedCreatorMethodWriter();

    @Override
    protected void process(TypeElement typeElement, Annotation annotation) {
        GenCreatorParser parser = new GenCreatorParser(typeElement);
        parser.read(annotation);

        String packageName = parser.getPackageName();
        String className = parser.getClassName();

        String parentClassName = parser.getParentClassName();

        TypeVariableName typeVariableName = TypeVariableName.get("T extends " + className);
        TypeSpec.Builder typeSpecBuilder = null;
        /**
         * 添加判断 是否直接使用生成的updater
         */
        boolean useDirect = false;
        if (annotation instanceof GenCreator) {
            useDirect = ((GenCreator)annotation).useDirect();
        } else if (annotation instanceof EnableGenForAggregate) {
            useDirect = ((EnableGenForAggregate)annotation).useCreatorDirect();
        } else if (annotation instanceof EnableGenForEntity) {
            useDirect = ((EnableGenForEntity)annotation).useCreatorDirect();
        }
        if (useDirect) {
            /**
             * 生成非抽象类
             */
            typeSpecBuilder = TypeSpec.classBuilder(className).addTypeVariable(typeVariableName)
                .addAnnotation(Data.class).addModifiers(Modifier.PUBLIC);
        } else {
            /**
             * 生成抽象类
             */
            typeSpecBuilder = TypeSpec.classBuilder(className).addTypeVariable(typeVariableName)
                .addAnnotation(Data.class).addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT);
        }

        if (StringUtils.isNotEmpty(parentClassName)) {
            ClassName parent = ClassName.bestGuess(parentClassName);
            TypeName typeName = TypeVariableName.get("T");
            ParameterizedTypeName parameterizedTypeName = ParameterizedTypeName.get(parent, typeName);
            typeSpecBuilder.superclass(parameterizedTypeName);
        }

        MethodSpec.Builder acceptMethodBuilder =
            MethodSpec.methodBuilder("accept").addModifiers(Modifier.PUBLIC).returns(TypeName.VOID)
                .addParameter(ParameterSpec.builder(TypeName.get(typeElement.asType()), "target").build());
        if (StringUtils.isNotEmpty(parentClassName)) {
            acceptMethodBuilder.addStatement("super.accept(target)");
        }

        CreatorMeta creatorMeta = this.metaParser.parse(typeElement);
        // this.getCreatorWriter(parser.getType()).writeTo(typeSpecBuilder, acceptMethodBuilder,
        // creatorMeta.getMethodMetas());

        this.getCreatorWriter(parser.getType()).writeTo(typeSpecBuilder, acceptMethodBuilder,
            creatorMeta.getMethodMetas(), findValidAnnotation(typeElement));
        typeSpecBuilder.addMethod(acceptMethodBuilder.build());

        getJavaSourceCollector().register(new JavaSource(packageName, className, typeSpecBuilder));
    }

    private Map<String, AnnotationMirror> findValidAnnotation(TypeElement typeElement) {
        Map<String, AnnotationMirror> annotationMap = new HashMap<>();
        List<VariableElement> fileds = ElementFilter.fieldsIn(typeElement.getEnclosedElements());
        for (VariableElement element : fileds) {
            if (!element.getModifiers().contains(Modifier.STATIC)) {
                putIfAbsent(element, annotationMap);
            }
        }
        return annotationMap;
    }

    private static final Class[] ANNOTATION_ARRAY = {Valid.class, NotBlank.class, NotEmpty.class, Length.class,
        Email.class, Range.class, ScriptAssert.class, URL.class, CreditCardNumber.class, ModCheck.class};

    private void putIfAbsent(VariableElement element, Map<String, AnnotationMirror> annotationMap) {

        List<AnnotationMirror> mirrors = (List<AnnotationMirror>)element.getAnnotationMirrors();
        for (AnnotationMirror mirror : mirrors) {
            for (Class clz : ANNOTATION_ARRAY) {
                if (mirror.getAnnotationType().asElement().asType().toString().equals(clz.getName())) {
                    annotationMap.put(element.getSimpleName().toString(), mirror);
                }
            }
        }
    }

    private CreatorWriter getCreatorWriter(GenCreatorType type) {
        switch (type) {
            case DATA_OPTION:
                return new DataOptionalBasedCreatorMethodWriter();
            case JAVA_BEAN:
                return new JavaBeanBasedCreatorMethodWriter();
        }
        return new JavaBeanBasedCreatorMethodWriter();
    }

    @Override
    public Class<Annotation>[] applyAnnCls() {
        return new Class[] {GenCreator.class, EnableGenForAggregate.class, EnableGenForEntity.class};
    }

    @Override
    public <A extends Annotation> Class<A>[] ignoreAnnCls() {
        return new Class[] {GenCreatorIgnore.class};
    }
}
